Skip to content

feat(agentos): add inspector Terminal tab with server-side shell replay - #1895

Merged
jog1t merged 4 commits into
mainfrom
feat/agentos-inspector-terminal-tab
Aug 5, 2026
Merged

feat(agentos): add inspector Terminal tab with server-side shell replay#1895
jog1t merged 4 commits into
mainfrom
feat/agentos-inspector-terminal-tab

Conversation

@jog1t

@jog1t jog1t commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Adds a Terminal tab to the agentOS dashboard inspector, plus the server-side state a reattaching client needs to be repainted.

Terminal tab

  • Interactive PTY shells rendered with Ghostty's VT parser (ghostty-web), multi-shell strip, close, resize.
  • Raw passthrough input — the kernel's line discipline owns echo, editing, and signals, so a local-echo layer would double-print and break raw-mode apps.
  • Shell enumeration is server-owned (listShells). A shell outlives the page that opened it and holds the VM awake, so it has to stay discoverable from a fresh page.
  • Opening a shell is explicit: the tab never boots a VM just because someone clicked it.

Shell replay

  • The actor keeps a headless emulator per live shell, fed the same chunks it broadcasts, and serializes it back to ANSI via a new observe-only shellSnapshot action. Without it, a reattaching client sees a blank pane until the guest repaints — which a full-screen program never does unprompted.
  • Wire taxonomy is none | screen | scrollback; all three implemented. Restores colors, attributes, cursor, alternate screen, and DEC modes (read back individually, so the exact mouse protocol and bracketed-paste state survive).
  • Every shellData broadcast carries a sequence number matching the snapshot's, so a client applies the repaint and then only the chunks past it — no gap, no double-render.
  • Bounded: 32 emulators, 1000 scrollback lines, 1000×1000 cells, with typed errors naming the limit.

Pre-existing fixes this depends on

  • Action transport — Chrome requires HTTP/2 for request streaming, so rivetkit's duplex: "half" made every inspector action fail with ERR_ALPN_NEGOTIATION_FAILED over cleartext HTTP/1.1.
  • Live eventsuseActor({name, id}) passed a field that does not exist, behind a type-suppressing cast; no tab received broadcasts.
  • System tab — threw on render for processes with no argv.
  • health.booted — permanently false; racing a settled promise against an immediate value always loses.

Tests

tests/shell-replay.test.ts (11 tests), including a fixed-point round-trip: replaying a snapshot into a fresh emulator must reproduce it byte-for-byte. One test covers a cross-shell content leak found during verification — a recycled emulator handle carried the previous shell's screen and served it to whoever attached next.

Verified live in the dashboard: real keyboard input, tab-switch and hard-reload repaint, alternate-screen fidelity, multi-shell isolation, clean close, no console errors.

Notes

jog1t added 3 commits August 6, 2026 00:25
agentOS runs Linux-only, so the engine, sidecar, and VMs live in a
container while the working tree stays on the host. node_modules, the
cargo target, and the toolchain build trees are container-owned volumes
so host edits are visible without dragging darwin-native binaries into
Linux.
…tem tab

- Buffer ReadableStream request bodies and strip `duplex`. Chrome
  requires HTTP/2 for request streaming, so every action POST failed
  with ERR_ALPN_NEGOTIATION_FAILED over cleartext HTTP/1.1.
- Attach by actor id via `getForId(...).connect()`. The previous
  `useActor({name, id})` call passed a field that does not exist behind
  a type-suppressing cast, so no tab received live events.
- Normalize omitted `args` in the process tree, which threw on render.
- Give the VM state chip the bordered pill treatment its neighbours use.
Interactive PTY shells in the dashboard, rendered with Ghostty's VT
parser. Input is raw passthrough: the kernel's line discipline owns
echo, editing, and signal generation.

Shell enumeration is server-owned. A shell outlives the page that opened
it and holds the VM awake, so a client that lost its page must still be
able to find it; `listShells` replaces any client-side tracking.

The actor also keeps a headless emulator per shell, fed the same chunks
it broadcasts, and serializes it back to ANSI on demand. Without it a
reattaching client sees a blank pane until the guest repaints, which a
full-screen program never does unprompted. Every broadcast carries a
sequence number matching the snapshot's, so a client applies the repaint
and then only the chunks past it.

Also fixes `health.booted`, which was permanently false: racing a
settled promise against an immediate value always loses, because
`.catch()` still schedules a microtask.
@railway-app

railway-app Bot commented Aug 5, 2026

Copy link
Copy Markdown

🚅 Deployed to the agentos-pr-1895 environment in agentos

Service Status Web Updated (UTC)
agentos ✅ Success (View Logs) Web Aug 5, 2026 at 11:09 pm

@railway-app
railway-app Bot temporarily deployed to agentos / agentos-pr-1895 August 5, 2026 22:30 Destroyed
@railway-app
railway-app Bot temporarily deployed to agentos / agentos-pr-1895 August 5, 2026 23:06 Destroyed
@jog1t
jog1t force-pushed the feat/agentos-inspector-terminal-tab branch from 2bb2f11 to 2291145 Compare August 5, 2026 23:07
@railway-app
railway-app Bot temporarily deployed to agentos / agentos-pr-1895 August 5, 2026 23:07 Destroyed
@railway-app
railway-app Bot temporarily deployed to agentos / agentos-pr-1896 August 5, 2026 23:08 Destroyed
@jog1t
jog1t force-pushed the feat/agentos-inspector-terminal-tab branch from 2291145 to fc70d78 Compare August 5, 2026 23:08
@railway-app
railway-app Bot temporarily deployed to agentos / agentos-pr-1895 August 5, 2026 23:08 Destroyed
Remove the globalThis.fetch override (fetch-compat.ts) that buffered
ReadableStream request bodies for HTTP/1.1 compatibility. Export
ShellSnapshot and ShellReplayMode from the package index so downstream
consumers (agentos-apps) can name them in generated declarations.
@jog1t
jog1t force-pushed the feat/agentos-inspector-terminal-tab branch from fc70d78 to 2b75cce Compare August 5, 2026 23:09
@railway-app
railway-app Bot temporarily deployed to agentos / agentos-pr-1895 August 5, 2026 23:09 Destroyed
@jog1t
jog1t merged commit 5b76f89 into main Aug 5, 2026
2 of 3 checks passed
@jog1t
jog1t deleted the feat/agentos-inspector-terminal-tab branch August 5, 2026 23:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant